(function() {
  var __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };

  window.Scene2D = (function(_super) {

    __extends(Scene2D, _super);

    function Scene2D(canvasId) {
      this.canvasId = canvasId;
      Scene2D.__super__.constructor.apply(this, arguments);
      this.canvas = document.getElementById(this.canvasId);
      this.context = this.canvas.getContext("2d");
      this.shapes = [];
      animation.bind(this, this.animationLoop);
    }

    Scene2D.prototype.add = function(shape) {
      this.shapes.push(shape);
      shape.context = this.context;
      shape.scene = this;
      return this.shapes = this.shapes.sort(function(a, b) {
        if (a.zIndex < b.zIndex) {
          return 1;
        } else if (a.zIndex > b.zIndex) {
          return -1;
        } else {
          return 0;
        }
      });
    };

    Scene2D.prototype.remove = function(shape) {
      return this.shapes.find(function(elem, index, arr) {
        if (shape === elem) {
          elem.context = null;
          elem.scene = null;
          return arr.splice(index, 1);
        }
      });
    };

    Scene2D.prototype.animationLoop = function(self, appTime) {
      var dt;
      if (self.lastTime) {
        dt = (appTime - self.lastTime) / 1000;
        self.shapes.each(function(elem, index, arr) {
          var cancel, eventArgs, force, location, newx, newy;
          force = elem.getComposedForce();
          elem.velocity.dx += force.ax * dt;
          elem.velocity.dy += force.ay * dt;
          if (typeof elem.location === "function") {
            location = elem.location();
          } else {
            location = elem.location;
          }
          newx = location.x + elem.velocity.dx * dt;
          eventArgs = {
            shape: elem,
            x: newx,
            y: location.y,
            width: elem.size.width,
            height: elem.size.height,
            time: appTime
          };
          cancel = !elem.notify("onmoving", eventArgs);
          if (!cancel) {
            if (typeof elem.location === "function") {
              elem.location(newx, null);
            } else {
              elem.location.x = newx;
            }
          }
          newy = location.y + elem.velocity.dy * dt;
          eventArgs = {
            shape: elem,
            x: location.x,
            y: newy,
            width: elem.size.width,
            height: elem.size.height,
            time: appTime
          };
          cancel = !elem.notify("onmoving", eventArgs);
          if (!cancel) {
            if (typeof elem.location === "function") {
              elem.location(null, newy);
            } else {
              elem.location.y = newy;
            }
          }
          return true;
        });
      }
      self.lastTime = appTime;
      return self.invalidate();
    };

    Scene2D.prototype.invalidate = function() {
      this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
      return this.shapes.each(function(elem, index, arr) {
        return elem.draw();
      });
    };

    Scene2D.prototype.getShapeAtPosition = function(x, y) {
      var shape;
      shape = null;
      this.shapes.each(function(elem, index, arr) {
        if (elem.location.x < x && elem.location.x + elem.size.width > x && elem.location.y < y && elem.location.y + elem.size.height > y) {
          if (!shape || shape.zIndex < elem.zIndex) {
            return shape = elem;
          }
        }
      });
      return shape;
    };

    return Scene2D;

  })(Observable);

  window.Shape2D = (function(_super) {

    __extends(Shape2D, _super);

    function Shape2D() {
      Shape2D.__super__.constructor.apply(this, arguments);
      this.type = "shape-2d";
      this.id = (new Date()).getTime();
      this.name = "Shape_" + this.id;
      this.scene = null;
      this.context = null;
      this.rotation = 0;
      this.forces = [];
      this.velocity = {
        dx: 0,
        dy: 0
      };
      this.location = {
        x: 0,
        y: 0
      };
      this.size = {
        width: 0,
        height: 0
      };
      this.dirty = false;
      this.zIndex = 0;
    }

    Shape2D.prototype.move = function(x, y) {
      if (!this.location.x === x || !this.location.y === y) {
        this.dirty = true;
      }
      this.location.x = x;
      return this.location.y = y;
    };

    Shape2D.prototype.moveX = function(x) {
      if (!this.location.x === x) {
        this.dirty = true;
      }
      return this.location.x = x;
    };

    Shape2D.prototype.moveY = function(y) {
      if (!this.location.x === y) {
        this.dirty = true;
      }
      return this.location.y = y;
    };

    Shape2D.prototype.addForce = function(id, ax, ay) {
      var force;
      force = {
        id: id,
        ax: ax,
        ay: ay
      };
      this.forces.push(force);
      return force;
    };

    Shape2D.prototype.getForce = function(id) {
      var force;
      force = null;
      this.forces.find(function(elem, index, arr) {
        if (elem.id === id) {
          return force = elem;
        }
      });
      if (force) {
        return force;
      } else {
        return this.addForce(id, 0, 0);
      }
    };

    Shape2D.prototype.setForceAx = function(id, ax) {
      var force;
      force = null;
      return this.forces.find(function(elem, index, arr) {
        if (elem.id === id) {
          return elem.ax = ax;
        }
      });
    };

    Shape2D.prototype.setForceAy = function(id, ay) {
      var force;
      force = null;
      return this.forces.find(function(elem, index, arr) {
        if (elem.id === id) {
          return elem.ay = ay;
        }
      });
    };

    Shape2D.prototype.removeForce = function(id) {
      this.forces.find(function(elem, index, arr) {
        if (elem.id === id) {
          return arr.splice(index, 1);
        }
      });
      return true;
    };

    Shape2D.prototype.getComposedForce = function() {
      var force;
      force = {
        id: "composed",
        ax: 0,
        ay: 0
      };
      this.forces.each(function(elem, index, arr) {
        if (elem.ax) {
          force.ax += elem.ax;
        }
        if (elem.ay) {
          return force.ay += elem.ay;
        }
      });
      return force;
    };

    Shape2D.prototype.intersects = function(location, size) {
      var aAboveB, aBelowB, aLeftOfB, aRightOfB, maxAx, maxAy, maxBx, maxBy, minAx, minAy, minBx, minBy;
      minAx = this.location.x;
      minAy = this.location.y;
      maxAx = this.location.x + this.size.width;
      maxAy = this.location.y + this.size.height;
      minBx = location.x;
      minBy = location.y;
      maxBx = location.x + size.width;
      maxBy = location.y + size.height;
      aLeftOfB = maxAx < minBx;
      aRightOfB = minAx > maxBx;
      aAboveB = minAy > maxBy;
      aBelowB = maxAy < minBy;
      return !(aLeftOfB || aRightOfB || aAboveB || aBelowB);
    };

    Shape2D.prototype.getBoundary = function() {
      var location, size;
      size = this.size;
      location = this.location;
      if (typeof location === "function") {
        location = this.location();
      }
      return [location.x, location.y, location.x + size.width, location.y + size.height];
    };

    Shape2D.prototype.boundaryIntersects = function(shape) {
      var aAboveB, aBelowB, aLeftOfB, aRightOfB, b1, b2, maxAx, maxAy, maxBx, maxBy, minAx, minAy, minBx, minBy;
      b1 = this.getBoundary();
      b2 = shape.getBoundary();
      minAx = b1[0];
      minAy = b1[1];
      maxAx = b1[2];
      maxAy = b1[3];
      minBx = b2[0];
      minBy = b2[1];
      maxBx = b2[2];
      maxBy = b2[3];
      aLeftOfB = maxAx < minBx;
      aRightOfB = minAx > maxBx;
      aAboveB = minAy > maxBy;
      aBelowB = maxAy < minBy;
      return !(aLeftOfB || aRightOfB || aAboveB || aBelowB);
    };

    Shape2D.prototype.getCollidingShapes = function() {
      var collidingShapes, self;
      collidingShapes = [];
      self = this;
      if (this.scene) {
        this.scene.shapes.each(function(elem, index, arr) {
          if (!(elem === self)) {
            if (self.boundaryIntersects(elem)) {
              return collidingShapes.push(elem);
            }
          }
        });
      }
      return collidingShapes;
    };

    Shape2D.prototype.draw = function() {};

    return Shape2D;

  })(Observable);

  window.GraphicShape2D = (function(_super) {

    __extends(GraphicShape2D, _super);

    function GraphicShape2D() {
      GraphicShape2D.__super__.constructor.apply(this, arguments);
      this.type = "graphics-shape-2d";
      this.stroke = {
        width: 1,
        color: "#000"
      };
      this.fill = {
        enabled: false,
        color: "#fff"
      };
    }

    GraphicShape2D.prototype.setFillStyles = function() {
      if (this.fill.enabled) {
        return this.context.fillStyle = this.fill.color;
      } else {
        return this.context.fillStyle = null;
      }
    };

    GraphicShape2D.prototype.setStrokeStyles = function() {
      this.context.lineWidth = this.stroke.width;
      return this.context.strokeStyle = this.stroke.color;
    };

    GraphicShape2D.prototype.setStyles = function() {
      this.setStrokeStyles();
      this.setFillStyles();
      return true;
    };

    GraphicShape2D.prototype.executeDraw = function() {
      this.context.stroke();
      if (this.fill.enabled) {
        return this.context.fill();
      }
    };

    return GraphicShape2D;

  })(Shape2D);

  window.Rectangle2D = (function(_super) {

    __extends(Rectangle2D, _super);

    function Rectangle2D(x, y, width, height) {
      Rectangle2D.__super__.constructor.apply(this, arguments);
      this.type = "rectangle-2d";
      this.location.x = x;
      this.location.y = y;
      this.size.width = width;
      this.size.height = height;
      this.borderRadius = 0;
    }

    Rectangle2D.prototype.draw = function() {
      this.setStyles();
      this.context.beginPath();
      if (this.borderRadius > 0) {
        this.context.roundRect(this.location.x, this.location.y, this.size.width, this.size.height, this.borderRadius);
      } else {
        this.context.rect(this.location.x, this.location.y, this.size.width, this.size.height);
      }
      this.context.closePath();
      return this.executeDraw();
    };

    return Rectangle2D;

  })(GraphicShape2D);

  window.Ellipse2D = (function(_super) {

    __extends(Ellipse2D, _super);

    function Ellipse2D(x, y, radiusX, radiusY) {
      Ellipse2D.__super__.constructor.apply(this, arguments);
      this.type = "ellipse-2d";
      this.location.x = x - radiusX;
      this.location.y = y - radiusY;
      this.size.width = 2 * radiusX;
      this.size.height = 2 * radiusY;
      this.center = {
        x: x,
        y: y
      };
      this.radiusX = radiusX;
      this.radiusY = radiusY;
    }

    Ellipse2D.prototype.draw = function() {
      var lx, magic, rx, ty, uy, xmagic, ymagic;
      if (this.context) {
        this.center = {
          x: this.location.x + this.radiusX,
          y: this.location.y + this.radiusY
        };
        this.setStyles();
        this.context.beginPath();
        lx = this.center.x - this.radiusX;
        rx = this.center.x + this.radiusX;
        ty = this.center.y - this.radiusY;
        uy = this.center.y + this.radiusY;
        magic = 0.551784;
        xmagic = magic * this.radiusX;
        ymagic = this.radiusY * magic;
        this.context.moveTo(this.center.x, ty);
        this.context.bezierCurveTo(this.center.x + xmagic, ty, rx, this.center.y - ymagic, rx, this.center.y);
        this.context.bezierCurveTo(rx, this.center.y + ymagic, this.center.x + xmagic, uy, this.center.x, uy);
        this.context.bezierCurveTo(this.center.x - xmagic, uy, lx, this.center.y + ymagic, lx, this.center.y);
        this.context.bezierCurveTo(lx, this.center.y - ymagic, this.center.x - xmagic, ty, this.center.x, ty);
        this.context.closePath();
        return this.executeDraw();
      }
    };

    return Ellipse2D;

  })(GraphicShape2D);

  window.Circle2D = (function(_super) {

    __extends(Circle2D, _super);

    function Circle2D(x, y, radius) {
      Circle2D.__super__.constructor.apply(this, arguments);
      this.type = "circle-2d";
      this.location.x = x - radius;
      this.location.y = y - radius;
      this.size.width = 2 * radius;
      this.size.height = 2 * radius;
      this.center = {
        x: x,
        y: y
      };
      this.radius = radius;
    }

    Circle2D.prototype.draw = function() {
      if (this.context) {
        this.context.beginPath();
        this.context.arc(this.location.x + this.radius, this.location.y + this.radius, this.radius, 0, 2 * Math.PI, false);
        return this.executeDraw();
      }
    };

    return Circle2D;

  })(GraphicShape2D);

  window.Line2D = (function(_super) {

    __extends(Line2D, _super);

    function Line2D(x, y, width, height) {
      Line2D.__super__.constructor.apply(this, arguments);
      this.type = "line-2d";
      this.location.x = x;
      this.location.y = y;
      this.size.width = width;
      this.size.height = height;
    }

    Line2D.prototype.draw = function() {
      if (this.context) {
        this.center = {
          x: this.location.x + this.radius,
          y: this.location.y + this.radius
        };
        this.setStyles();
        this.context.beginPath();
        this.context.moveTo(this.location.x, this.location.y);
        this.context.lineTo(this.location.x + this.size.width, this.location.y + this.size.height);
        this.context.closePath();
        return this.executeDraw();
      }
    };

    return Line2D;

  })(GraphicShape2D);

  window.Arrow2D = (function(_super) {

    __extends(Arrow2D, _super);

    function Arrow2D(x, y, width, height) {
      Arrow2D.__super__.constructor.apply(this, arguments);
      this.type = "arrow-2d";
      this.location.x = x;
      this.location.y = y;
      this.size.width = width;
      this.size.height = height;
      this.as = Math.sqrt(height * height + width * width) / 20;
      this.angle = Math.atan2(height, width);
    }

    Arrow2D.prototype.draw = function() {
      if (this.context) {
        this.setStyles();
        this.context.beginPath();
        this.context.moveTo(this.location.x, this.location.y);
        this.context.lineTo(this.location.x + this.size.width, this.location.y + this.size.height);
        this.context.lineTo(this.location.x + this.size.width - this.as * Math.cos(this.angle - Math.PI / 6), this.location.y + this.size.height - this.as * Math.sin(this.angle - Math.PI / 6));
        this.context.moveTo(this.location.x + this.size.width, this.location.y + this.size.height);
        this.context.lineTo(this.location.x + this.size.width - this.as * Math.cos(this.angle + Math.PI / 6), this.location.y + this.size.height - this.as * Math.sin(this.angle + Math.PI / 6));
        return this.executeDraw();
      }
    };

    return Arrow2D;

  })(GraphicShape2D);

  window.PolyLine2D = (function(_super) {

    __extends(PolyLine2D, _super);

    function PolyLine2D(points) {
      var self;
      PolyLine2D.__super__.constructor.apply(this, arguments);
      this.type = "polyline-2d";
      this.points = new Array();
      if (typeof points === "object" && points.length) {
        self = this;
        this.location.x = null;
        this.location.y = null;
        this.size.width = 0;
        this.size.height = 0;
        this.points = [];
        points.each(function(elem, index, array) {
          if (elem && elem.x && elem.y && typeof elem.x === "number" && typeof elem.y === "number") {
            self.points.push(elem);
            if (!self.location.x || self.location.x > elem.x) {
              self.location.x = elem.x;
            }
            if (!self.location.y || self.location.x > elem.y) {
              self.location.y = elem.y;
            }
            if (index > 0 && Math.abs(array[index - 1].x - elem.x) > self.size.width) {
              self.size.width = Math.abs(array[index - 1].x - elem.x);
            }
            if (index > 0 && Math.abs(array[index - 1].y - elem.y) > self.size.height) {
              return self.size.height = Math.abs(array[index - 1].y - elem.y);
            }
          }
        });
      }
    }

    PolyLine2D.prototype.draw = function() {
      var self;
      if (this.context) {
        this.setStyles();
        this.context.beginPath();
        self = this;
        this.points.each(function(elem, index, array) {
          if (index === 0) {
            return self.context.moveTo(elem.x, elem.y);
          } else {
            return self.context.lineTo(elem.x, elem.y);
          }
        });
        this.context.closePath();
        return this.executeDraw();
      }
    };

    return PolyLine2D;

  })(GraphicShape2D);

  window.Image2D = (function(_super) {

    __extends(Image2D, _super);

    function Image2D(x, y, width, height, image) {
      Image2D.__super__.constructor.apply(this, arguments);
      this.type = "image-2d";
      this.location.x = x;
      this.location.y = y;
      this.size.width = width;
      this.size.height = height;
      this.changeImage(image);
    }

    Image2D.prototype.changeImage = function(image) {
      var img, self;
      if (typeof image === "object" && image.tagName === "IMG") {
        return this.image = image;
      } else {
        if (typeof image === "string") {
          self = this;
          img = new Image();
          img.onload = function() {
            return self.image = this;
          };
          return img.src = image;
        } else {
          return false;
        }
      }
    };

    Image2D.prototype.draw = function() {
      if (this.image && this.context) {
        return this.context.drawImage(this.image, this.location.x, this.location.y, this.size.width, this.size.height);
      }
    };

    return Image2D;

  })(Shape2D);

  window.PartialImage2D = (function(_super) {

    __extends(PartialImage2D, _super);

    function PartialImage2D(x, y, width, height, image) {
      PartialImage2D.__super__.constructor.apply(this, arguments);
      this.type = "image-2d";
      this.location.x = x;
      this.location.y = y;
      this.size.width = width;
      this.size.height = height;
      this.clippingRectangle = {
        x: 0,
        y: 0,
        width: this.size.width,
        height: this.size.height
      };
      this.changeImage(image);
    }

    PartialImage2D.prototype.changeImage = function(image) {
      var img, self;
      if (typeof image === "object" && image.tagName === "IMG") {
        return this.image = image;
      } else {
        if (typeof image === "string") {
          self = this;
          img = new Image();
          img.onload = function() {
            return self.image = this;
          };
          return img.src = image;
        } else {
          return false;
        }
      }
    };

    PartialImage2D.prototype.setClippingRectangle = function(x, y, width, height) {
      return this.clippingRectangle = {
        x: x,
        y: y,
        width: width,
        height: height
      };
    };

    PartialImage2D.prototype.draw = function() {
      if (this.image && this.context) {
        return this.context.drawImage(this.image, this.clippingRectangle.x, this.clippingRectangle.y, this.clippingRectangle.width, this.clippingRectangle.height, this.location.x, this.location.y, this.size.width, this.size.height);
      }
    };

    return PartialImage2D;

  })(Shape2D);

  window.ImageSprite2D = (function(_super) {

    __extends(ImageSprite2D, _super);

    function ImageSprite2D(x, y, width, height, spriteRows, spriteCols, spriteWidth, spriteHeight, image) {
      ImageSprite2D.__super__.constructor.apply(this, arguments);
      this.type = "image-sprite-2d";
      this.spriteWidth = spriteWidth;
      this.spriteHeight = spriteHeight;
      this.spriteRows = spriteRows;
      this.spriteCols = spriteCols;
      this.spriteRowIndex = 0;
      this.spriteColIndex = 0;
      this.location.x = x;
      this.location.y = y;
      this.size.width = width;
      this.size.height = height;
      this.changeImage(image);
    }

    ImageSprite2D.prototype.changeImage = function(image) {
      var img, self;
      if (typeof image === "object" && image.tagName === "IMG") {
        return this.image = image;
      } else {
        if (typeof image === "string") {
          self = this;
          img = new Image();
          img.onload = function() {
            return self.image = this;
          };
          return img.src = image;
        } else {
          return false;
        }
      }
    };

    ImageSprite2D.prototype.setCurrentSprite = function(rowIndex, colIndex) {
      this.spriteRowIndex = rowIndex;
      return this.spriteColIndex = colIndex;
    };

    ImageSprite2D.prototype.setSpriteRow = function(rowIndex) {
      if (rowIndex < this.spriteRows && rowIndex >= 0) {
        return this.spriteRowIndex = rowIndex;
      }
    };

    ImageSprite2D.prototype.setSpriteCol = function(colIndex) {
      if (rowIndex < this.spriteCols && colIndex > 0) {
        return this.spriteColIndex = colIndex;
      }
    };

    ImageSprite2D.prototype.setNextSpriteOnRow = function() {
      if (this.spriteColIndex < this.spriteCols - 1) {
        return this.spriteColIndex++;
      } else {
        return this.spriteColIndex = 0;
      }
    };

    ImageSprite2D.prototype.setPrevSpriteOnRow = function() {
      if (this.spriteColIndex > 0) {
        return this.spriteColIndex--;
      } else {
        return this.spriteColIndex = this.spriteCols - 1;
      }
    };

    ImageSprite2D.prototype.draw = function() {
      if (this.image && this.context) {
        return this.context.drawImage(this.image, this.spriteWidth * this.spriteColIndex, this.spriteHeight * this.spriteRowIndex, this.spriteWidth, this.spriteHeight, this.location.x, this.location.y, this.size.width, this.size.height);
      }
    };

    return ImageSprite2D;

  })(Shape2D);

  window.ComposedShape2D = (function(_super) {

    __extends(ComposedShape2D, _super);

    function ComposedShape2D(x, y) {
      ComposedShape2D.__super__.constructor.apply(this, arguments);
      this.type = "composed-2d";
      this.location.x = x;
      this.location.y = y;
      this.size = {
        width: 0,
        height: 0
      };
      this.shapes = [];
    }

    ComposedShape2D.prototype.add = function(shape) {
      this.shapes.push(shape);
      this.shapes = this.shapes.sort(function(a, b) {
        if (a.zIndex < b.zIndex) {
          return 1;
        } else if (a.zIndex > b.zIndex) {
          return -1;
        } else {
          return 0;
        }
      });
      return this.updateSize();
    };

    ComposedShape2D.prototype.remove = function(shape) {
      this.shapes.find(function(elem, index, arr) {
        if (shape === elem) {
          return arr.splice(index, 1);
        }
      });
      return this.updateSize();
    };

    ComposedShape2D.prototype.updateSize = function() {
      var maxHeight, maxWidth;
      maxWidth = 0;
      maxHeight = 0;
      this.shapes.each(function(elem, index, arr) {
        if (elem.location.x + elem.size.width > maxWidth) {
          maxWidth = elem.location.x + elem.size.width;
        }
        if (elem.location.y + elem.size.height > maxHeight) {
          return maxHeight = elem.location.y + elem.size.height;
        }
      });
      this.size.width = maxWidth;
      return this.size.height = maxHeight;
    };

    ComposedShape2D.prototype.draw = function() {
      var self;
      if (this.context) {
        self = this;
        return this.shapes.each(function(elem, index, arr) {
          var oldLocation;
          oldLocation = {
            x: elem.location.x,
            y: elem.location.y
          };
          elem.move(oldLocation.x + self.location.x, oldLocation.y + self.location.y);
          elem.context = self.context;
          elem.draw();
          return elem.move(oldLocation.x, oldLocation.y);
        });
      }
    };

    return ComposedShape2D;

  })(Shape2D);

  window.Polygon2D = (function(_super) {

    __extends(Polygon2D, _super);

    function Polygon2D(points) {
      var firstPoint, lastPoint;
      Polygon2D.__super__.constructor.apply(this, arguments);
      this.type = "polygon-2d";
      if (this.points.length > 1) {
        firstPoint = this.points[0];
        lastPoint = this.points[this.points.length - 1];
        if (!(firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y)) {
          this.points.push({
            x: firstPoint.x,
            y: firstPoint.y
          });
        }
      }
      this.position = {
        x: this.location.x,
        y: this.location.y
      };
      this.location = function(x, y) {
        var dx, dy;
        if (!x && !y) {
          return this.position;
        } else {
          if (x) {
            dx = x - this.position.x;
            this.position.x = x;
          }
          if (y) {
            dy = y - this.position.y;
            this.position.y = y;
          }
          return this.points.each(function(elem, index, arr) {
            if (x) {
              elem.x += dx;
            }
            if (y) {
              return elem.y += dy;
            }
          });
        }
      };
    }

    Polygon2D.prototype.getBoundary = function() {
      var maxx, maxy, minx, miny, self;
      if (!this.points || !this.points.length) {
        return [0, 0, 0, 0];
      } else {
        minx = this.points[0].x;
        maxx = this.points[0].x;
        miny = this.points[0].y;
        maxy = this.points[0].y;
        self = this;
        this.points.each(function(elem, index, arr) {
          if (elem.x < minx) {
            minx = elem.x;
          }
          if (elem.x > maxx) {
            maxx = elem.x;
          }
          if (elem.y < miny) {
            miny = elem.y;
          }
          if (elem.y > maxy) {
            return maxy = elem.y;
          }
        });
        return [minx, miny, maxx, maxy];
      }
    };

    Polygon2D.prototype.getCollidingShapes = function() {
      var newShapes, self, shapes;
      shapes = Polygon2D.__super__.getCollidingShapes.call(this);
      if (shapes && shapes.length && shapes.length > 0) {
        self = this;
        newShapes = [];
        shapes.each(function(shape, index, arr) {
          var collider;
          collider = new Polygon2DCollitionSAT(self, shape);
          if (collider.collides()) {
            return newShapes.push(shape);
          }
        });
        shapes = newShapes;
      }
      return shapes;
    };

    return Polygon2D;

  })(PolyLine2D);

  window.PolygonTriangle2D = (function(_super) {

    __extends(PolygonTriangle2D, _super);

    function PolygonTriangle2D(x1, y1, x2, y2, x3, y3) {
      var points;
      points = [
        {
          x: x1,
          y: y1
        }, {
          x: x2,
          y: y2
        }, {
          x: x3,
          y: y3
        }, {
          x: x1,
          y: y1
        }
      ];
      PolygonTriangle2D.__super__.constructor.call(this, points);
    }

    return PolygonTriangle2D;

  })(Polygon2D);

  window.PolygonRectangle2D = (function(_super) {

    __extends(PolygonRectangle2D, _super);

    function PolygonRectangle2D(x, y, width, height) {
      var points;
      points = [
        {
          x: x,
          y: y
        }, {
          x: x + width,
          y: y
        }, {
          x: x + width,
          y: y + width
        }, {
          x: x,
          y: y + height
        }, {
          x: x,
          y: y
        }
      ];
      PolygonRectangle2D.__super__.constructor.call(this, points);
    }

    return PolygonRectangle2D;

  })(Polygon2D);

  window.PolygonPentagon2D = (function(_super) {

    __extends(PolygonPentagon2D, _super);

    function PolygonPentagon2D(x, y, l) {
      var point1, point2, point3, point4, point5, points;
      points = [];
      point1 = {
        x: x,
        y: y
      };
      point2 = {
        x: x,
        y: y + l
      };
      point3 = {
        x: point2.x + Math.sin(72 * Math.PI / 180) * l,
        y: point2.y + Math.cos(72 * Math.PI / 180) * l
      };
      point4 = {
        x: point3.x + Math.cos(54 * Math.PI / 180) * l,
        y: point3.y - Math.sin(54 * Math.PI / 180) * l
      };
      point5 = {
        x: point1.x + Math.sin(72 * Math.PI / 180) * l,
        y: point1.y - Math.cos(72 * Math.PI / 180) * l
      };
      PolygonPentagon2D.__super__.constructor.call(this, [point1, point2, point3, point4, point5]);
    }

    return PolygonPentagon2D;

  })(Polygon2D);

  window.PolygonHexagon2D = (function(_super) {

    __extends(PolygonHexagon2D, _super);

    function PolygonHexagon2D(x, y, l) {
      var p1, p2, p3, p4, p5, p6, points;
      points = [];
      p1 = {
        x: x,
        y: y
      };
      p2 = {
        x: p1.x - Math.cos(30 * Math.PI / 180) * l,
        y: p1.y + Math.sin(30 * Math.PI / 180) * l
      };
      p3 = {
        x: p2.x,
        y: p2.y + l
      };
      p4 = {
        x: p3.x + Math.cos(30 * Math.PI / 180) * l,
        y: p3.y + Math.sin(30 * Math.PI / 180) * l
      };
      p5 = {
        x: p4.x + Math.cos(30 * Math.PI / 180) * l,
        y: p4.y - Math.sin(30 * Math.PI / 180) * l
      };
      p6 = {
        x: p5.x,
        y: p5.y - l
      };
      PolygonHexagon2D.__super__.constructor.call(this, [p1, p2, p3, p4, p5, p6]);
    }

    return PolygonHexagon2D;

  })(Polygon2D);

  window.Vector = (function() {

    function Vector(vector) {
      this.vector = vector;
    }

    Vector.prototype.substract = function(vector) {
      var self;
      if (!this.vector || !this.vector.length) {
        return null;
      } else {
        if (!vector || !vector.length || !vector.length === this.vector.length) {
          return null;
        } else {
          self = this;
          return this.vector.each(function(elem, index, arr) {
            return self.vector[index] -= vector[index];
          });
        }
      }
    };

    Vector.prototype.invert = function() {
      return this.vector.each(function(elem, index, arr) {
        var aux;
        if (index < arr.length / 2) {
          aux = arr[index];
          arr[index] = arr[arr.length - 1 - index];
          return arr[arr.length - 1 - index] = aux;
        }
      });
    };

    Vector.prototype.perp = function() {
      this.invert();
      return this.vector[0] = -this.vector[0];
    };

    Vector.prototype.dotProduct = function(vector) {
      var dot, self;
      if (!this.vector || !this.vector.length) {
        return 0;
      } else {
        if (!vector || !vector.length || !vector.length === this.vector.length) {
          return 0;
        } else {
          dot = 0;
          self = this;
          this.vector.each(function(elem, index, arr) {
            return dot += self.vector[index] * vector[index];
          });
          return dot;
        }
      }
    };

    return Vector;

  })();

  window.Polygon2DCollitionSAT = (function() {

    function Polygon2DCollitionSAT(shape1, shape2) {
      this.shape1 = shape1;
      this.shape2 = shape2;
      this.valid = false;
      if (this.shape1.type === "polygon-2d" && this.shape2.type === "polygon-2d") {
        this.valid = true;
      }
    }

    Polygon2DCollitionSAT.prototype.getProjectionAxes = function(shape) {
      var axes;
      axes = [];
      shape.points.each(function(elem, index, arr) {
        var vector1, vector2;
        if (!(index === arr.length - 1)) {
          vector1 = new Vector([elem.x, elem.y]);
          vector2 = new Vector([arr[index + 1].x, arr[index + 1].y]);
          vector1.substract(vector2.vector);
          vector1.perp();
          return axes.push(vector1);
        }
      });
      return axes;
    };

    Polygon2DCollitionSAT.prototype.getPolygonProjection = function(shape, axis) {
      var max, min;
      min = axis.dotProduct([shape.points[0].x, shape.points[0].y]);
      max = min;
      shape.points.each(function(elem, index, arr) {
        var val;
        if (index > 0) {
          val = axis.dotProduct([elem.x, elem.y]);
          if (min > val) {
            min = val;
          }
          if (max < val) {
            return max = val;
          }
        }
      });
      return [min, max];
    };

    Polygon2DCollitionSAT.prototype.collides = function() {
      var axes1, axes2, free, self;
      if (!this.valid) {
        return true;
      } else {
        self = this;
        axes1 = this.getProjectionAxes(this.shape1);
        axes2 = this.getProjectionAxes(this.shape2);
        free = false;
        axes1.each(function(elem, index, arr) {
          var axis, overlap, p1, p2;
          axis = elem;
          p1 = self.getPolygonProjection(self.shape1, axis);
          p2 = self.getPolygonProjection(self.shape2, axis);
          overlap = false;
          if (p1[0] > p2[0] && p1[0] < p2[1]) {
            overlap = true;
          }
          if (p2[0] > p1[0] && p2[0] < p1[1]) {
            overlap = true;
          }
          if (!overlap) {
            return free = true;
          }
        });
        if (!free) {
          axes2.each(function(elem, index, arr) {
            var axis, overlap, p1, p2;
            axis = elem;
            p1 = self.getPolygonProjection(self.shape1, axis);
            p2 = self.getPolygonProjection(self.shape2, axis);
            overlap = false;
            if (p1[0] > p2[0] && p1[0] < p2[1]) {
              overlap = true;
            }
            if (p2[0] > p1[0] && p2[0] < p1[1]) {
              overlap = true;
            }
            if (!overlap) {
              return free = true;
            }
          });
        }
        return !free;
      }
    };

    return Polygon2DCollitionSAT;

  })();

}).call(this);
